/**
 * 
 */
package edu.umd.clip.lm.tools;

import java.io.*;
import java.nio.charset.Charset;
import java.text.*;
import java.util.*;
import java.util.regex.*;

import edu.berkeley.nlp.util.*;
import edu.umd.clip.lm.factors.*;
import edu.umd.clip.lm.factors.Dictionary;
import edu.umd.clip.lm.model.*;

/**
 * @author Denis Filimonov <den@cs.umd.edu>
 *
 */
public class EvalTagger {
	public static class Options {
        @Option(name = "-config", required = true, usage = "XML config file")
		public String config;
        @Option(name = "-test", required = false, usage = "Test file (default: stdin)")
		public String test;
        @Option(name = "-gold", usage = "Gold file")
		public String gold;
        @Option(name = "-tagFactor", required = false, usage = "Tag Factor (default: T)")
		public String tagFactor = "T";
	}

	private static final Pattern pairs_re = Pattern.compile("\\s+");
	private static final Pattern pair_re = Pattern.compile("^(.+)/([^/]+)$");
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
        OptionParser optParser = new OptionParser(Options.class);
        final Options opts = (Options) optParser.parse(args, true);

		Experiment.initialize(opts.config);
		Experiment experiment = Experiment.getInstance();
		FactorTupleDescription desc = experiment.getTupleDescription();
		
		byte tagIdx = desc.getFactorIndex(opts.tagFactor);
		byte wordIdx = desc.getMainFactorIndex();
		
		int totalWords = 0;
		int totalCorrect = 0;
		int unkWords = 0;
		int unkCorrect = 0;
		
		try {
			BufferedReader goldReader = new BufferedReader(new InputStreamReader(new FileInputStream(opts.gold), Charset.forName("UTF-8")));
			BufferedReader testReader = new BufferedReader(new InputStreamReader(opts.test == null ? System.in : new FileInputStream(opts.test), 
				Charset.forName("UTF-8")));

			String goldLine;
			String testLine;
			while(true) {
				testLine = testReader.readLine();
				if (testLine == null) break;
				testLine = testLine.trim();
				
				goldLine = goldReader.readLine();
				if (goldLine == null) break;
				goldLine = goldLine.trim();
				
				ArrayList<FactorTuple> testTags = parseTaggedLine(testLine, tagIdx);
				ArrayList<FactorTuple> goldTags = parseTaggedLine(goldLine, tagIdx);
				
				assert(testTags.size() == goldTags.size());
				for(int i=0; i<goldTags.size(); ++i) {
					FactorTuple gold = goldTags.get(i);
					FactorTuple test = testTags.get(i);
					boolean isCorrect = gold.getValue(tagIdx) == test.getValue(tagIdx);
					boolean isUnk = Dictionary.isUnk(gold.getValue(wordIdx));

					++totalWords;
					if (isCorrect) {
						++totalCorrect;
					}
					if (isUnk) {
						++unkWords;
						if (isCorrect) {
							++unkCorrect;
						}
					}
				}
			}
			
			NumberFormat nf = new DecimalFormat("0.##");
			System.out.printf("Total Tags: %d (%s%% unknown), correct %s%% (%d)\n", 
					totalWords, nf.format(100.0 * unkWords / totalWords), 
					nf.format(100.0 * totalCorrect / totalWords), totalCorrect);
			System.out.printf("Known %d, %s%% correct, Unknown %d, %s%% correct\n", 
					(totalWords - unkWords), nf.format(100.0 * (totalCorrect - unkCorrect) / (totalWords - unkWords)), 
					unkWords, nf.format(100.0 * unkCorrect / unkWords));
		} catch(IOException e) {
			
		}
	}

	private static ArrayList<FactorTuple> parseTaggedLine(String line, byte tagIdx) {
		Experiment experiment = Experiment.getInstance();
		FactorTupleDescription desc = experiment.getTupleDescription();
		
		Dictionary wordDict = desc.getDictionary(desc.getMainFactorIndex());
		Dictionary tagDict = desc.getDictionary(tagIdx);
		
		String[] pairs = pairs_re.split(line);
		ArrayList<FactorTuple> tuples = new ArrayList<FactorTuple>(pairs.length);
		
		for(String pair : pairs) {
			Matcher matcher = pair_re.matcher(pair);
			if (matcher.matches()) {
				String word = matcher.group(1);
				String tag = matcher.group(2);
				
				FactorTuple tuple = new FactorTuple();
				tuple.setValue(desc.getMainFactorIndex(), wordDict.getId(word));
				tuple.setValue(tagIdx, tagDict.getId(tag));
				tuples.add(tuple);
			}
		}
		return tuples;
	}
}
